package org.xyf.IM.config;

import com.alibaba.nacos.api.naming.NamingService;
import com.google.common.collect.ImmutableMap;
import io.grpc.*;
import io.grpc.internal.GrpcAttributes;
import io.micrometer.core.instrument.binder.grpc.ObservationGrpcClientInterceptor;
import io.micrometer.observation.ObservationRegistry;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Lazy;
import org.springframework.grpc.client.ChannelBuilderOptions;
import org.springframework.grpc.client.GrpcChannelFactory;
import org.springframework.util.StringUtils;
import org.xyf.IM.utils.SpringUtils;
import org.xyf.grpcService.UserServiceGrpc;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URI;
import java.util.*;

@Configuration(proxyBeanMethods = false)
@Slf4j
public class GrpcConfig {


    @Value("${spring.grpc.client.channels.user-service.address}")
    private String userServiceAddr;

    private static final String DEFAULT_SCHEME = "nacos";


    @Bean
    @DependsOn({"springUtils"}) //必不可少，要保证此bean在SpringUtils装配完成之后再装配
    UserServiceGrpc.UserServiceBlockingStub stub(GrpcChannelFactory channels) {
        //默认负载均衡策略为
        ManagedChannel channel = channels.createChannel(userServiceAddr);
        return UserServiceGrpc.newBlockingStub(channel);
    }

    public static class CustomNameResolver extends NameResolver {

        private final URI uri;
        private final Map<String, List<InetSocketAddress>> addrStore;
        private Listener2 listener;

        private DiscoveryClient discoveryClient;

        private String serviceName;

        //targetUri 对他创建channel时的参数
        public CustomNameResolver(URI targetUri) {
            log.info("CustomNameResolver targetUri is:{}",targetUri);
            this.uri = targetUri;

            String schema = targetUri.getScheme();
            if("nacos".equals(schema)){
                String path = uri.getPath();
                serviceName = path.startsWith("/") ? path.substring(1) : path;
            }
            addrStore = this.getAddrStoreFromNacos(serviceName);
        }

        // Micrometer会使用Authority作为peerName
        @Override
        public String getServiceAuthority() {
            // Be consistent with behavior in grpc-go, authority is saved in Host field of URI.
//            if (uri.getHost() != null) {
//                return uri.getHost();
//            }
            if(StringUtils.hasLength(serviceName)){
                return serviceName;
            }
            return "no host";
        }

        @Override
        public void shutdown() {
        }

        @Override
        public void start(Listener2 listener) {
            this.listener = listener;
            this.resolve();
        }

        @Override
        public void refresh() {
            this.resolve();
        }

        //从注册中心中获取实例列表并通过listener转递给LoadBalancer
        private void resolve() {
            // 1. 直接从Nacos获取服务实例列表（假设已注入DiscoveryClient）

            Map<String, List<InetSocketAddress>> fromNacos = this.getAddrStoreFromNacos(serviceName);
            List<InetSocketAddress> addressList = fromNacos.get(serviceName);

            List<EquivalentAddressGroup> addressGroups = new ArrayList<>(addressList.size());
            for(InetSocketAddress address:addressList){
//                // ✅ 1. 构造 peerName
//                String peerName = "ipv4:" + address.getHostName() + ":" + address.getPort();
//                Attributes attributes = Attributes.newBuilder()
//                        .set(GrpcAttributes.ATTR_AUTHORITY_VERIFIER_)
//                        .build();
                EquivalentAddressGroup addressGroup = new EquivalentAddressGroup(address);
                addressGroups.add(addressGroup);

            }
            ResolutionResult result = ResolutionResult.newBuilder()
                    .setAddressesOrError(StatusOr.fromValue(addressGroups))
                    .build();
            listener.onResult(result);//传递给负载均衡器
        }

        private Map<String, List<InetSocketAddress>> getAddrStoreFromNacos(String serviceName){
            if(Objects.isNull(discoveryClient)){
                discoveryClient = SpringUtils.getBean(DiscoveryClient.class);
            }
            List<ServiceInstance> instances = discoveryClient.getInstances(serviceName);

            List<InetSocketAddress> addressList = new ArrayList<>();
            for(ServiceInstance instance:instances){
                String grpcPort = instance.getMetadata().get("grpc-port");
                if(StringUtils.hasLength(grpcPort)){
                    InetSocketAddress address = new InetSocketAddress(instance.getHost(), Integer.parseInt(grpcPort));
                    addressList.add(address);
                } else {
                    throw new RuntimeException("grpc-port不能为空;请检查此provider是否在yml中配置了grpc-port参数!");
                }
            }
            return ImmutableMap.<String, List<InetSocketAddress>>builder()
                    .put(serviceName,addressList)
                    .build();
        }

        private SocketAddress toSocketAddress(InetSocketAddress address) {
            return new InetSocketAddress(address.getHostName(), address.getPort());
        }

        private EquivalentAddressGroup addrToEquivalentAddressGroup(List<SocketAddress> addrList) {
            return new EquivalentAddressGroup(addrList);
        }
    }

    public static class CustomNameResolverProvider extends NameResolverProvider {

        @Override
        public NameResolver newNameResolver(URI targetUri, NameResolver.Args args) {
            return new CustomNameResolver(targetUri);
        }

        @Override
        protected boolean isAvailable() {
            return true;
        }

        @Override
        protected int priority() {
            return 5;
        }

        @Override
        // gRPC choose the first NameResolverProvider that supports the target URI scheme.
        public String getDefaultScheme() {
            return DEFAULT_SCHEME;
        }
    }

}
